<template>
  <el-form
    ref="dataForm"
    :model="formData"
    label-width="120px"
    :rules="props.rules"
    label-position="right"
    :disabled="props.disabled"
    :hide-required-asterisk="props.disabled"
  >
    <div v-if="rowFields.length > 0">
      <el-row v-for="(colFields, index) in rowFields" :key="index">
        <el-col
          v-for="(field, index) in colFields"
          :key="index"
          :span="(24 / props.cols) * (field.colspan || 1)"
        >
          <el-form-item
            v-if="typeof field.type === 'undefined' || field.type !== 'blank'"
            :label="field.label"
            :prop="field.prop"
          >
            <component
              :is="'cm-' + (field.type || 'input')"
              v-model="formData[field.prop]"
              v-bind="cleanProp(field)"
            />
          </el-form-item>
        </el-col>
      </el-row>
    </div>
  </el-form>
</template>

<script setup>
import {onMounted, onUnmounted, ref, watch} from 'vue';
import tool from '@/utils/tool';

const emit = defineEmits(['update:modelValue']);
const props = defineProps({
  modelValue: {
    type: Object,
    default: () => {}
  },
  fields: Array,
  rules: Object,
  disabled: {
    type: Boolean,
    default: false,
  },
  cols: {
    type: Number,
    default: 2
  }
});

const dataForm = ref();
const formData = ref({});
const rowFields = ref([]);
const cleanProps = ref(['prop', 'label', 'type', 'table', 'sortable', 'tooltip']);

const initFormData = () => {
  formData.value = Object.assign({}, props.modelValue || {});
  rowFields.value = calcRowFields(formData.value) || [];
  dataForm.value && dataForm.value.clearValidate();
};

const calcRowFields = (json) => {
  let fields = [];
  props.fields.filter(item => props.disabled || (!props.disabled && item.onlyShow !== true)).forEach(field => {
    fields.push(Object.assign({}, field));
  });

  let rowFields = [];
  let colFields = [];
  let itemColspan = 0;
  for (let field of fields) {
    for (let prop in field) {
      if (typeof field[prop] === 'function') {
        field[prop] = field[prop](json);
      }
    }

    let colspan = field.colspan || 1;
    itemColspan += colspan;
    colFields.push(field);

    if (itemColspan >= props.colspan) {
      rowFields.push(colFields);
      colFields = [];
      itemColspan = 0;
    }
  }
  if (colFields.length > 0) {
    rowFields.push(colFields);
  }
  return rowFields;
};

const cleanProp = (field) => {
  let result = {};
  for (let prop in field) {
    if (cleanProps.value.indexOf(prop) < 0) {
      result[prop] = field[prop];
    }
  }
  return result;
};

const validate = async(callback) => {
  callback(await dataForm.value.validate());
};

const clearValidate = () => {
  dataForm.value.clearValidate()
};

watch(() => props, () => {
  initFormData();
}, {deep: true});

watch(() => formData.value, (val) => {
  if (!tool.isDeepEqual(val, props.modelValue)) {
    emit('update:modelValue', val);
  }
}, { deep: true});

onMounted(() => {
  initFormData();
});

onUnmounted(() => {
  formData.value = {};
  rowFields.value = [];
});

defineExpose({
  validate,
  clearValidate
});
</script>